﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vibz.Common.Channel;
using System.Windows.Forms;
using System.Threading;
using Vibz.Common.Model;
using System.Reflection;
using Vibz.Common.Notification;

namespace Vibz.Common
{
    public abstract class ClientTerminal : Form
    {
        protected ModelBase Model;
        protected Logs Log { get; set; }
        protected SocketClient SocketClient { get; set; }
        private System.Windows.Forms.Timer timer1;
        private System.ComponentModel.IContainer components = null;
        bool _isFirstLoad = true;
        bool _doNotifyServer = true;
        public string ClientID
        {
            get
            {
                if (SocketClient != null) return SocketClient.ID; else return "";
            }
        }
        void socketClient_DataReceived(System.Net.Sockets.Socket socket, byte[] receivedData, EventArgs e)
        {
            try
            {
                List<KeyValuePair<DataPacketHeader, string>> packets = DataPacket.Segregate(receivedData);
                if (Model == null)
                    Model = ModelBase.Empty;

                _doNotifyServer = false;
                lock (Model)
                {
                    foreach (KeyValuePair<DataPacketHeader, string> packet in packets)
                    {
                        MethodInfo method = this.GetType().GetMethod("Merge");
                        MethodInfo genericMethod = method.MakeGenericMethod(Model.GetType());
                        genericMethod.Invoke(this, new object[] { Encoding.ASCII.GetBytes(packet.Value) });
                    }
                    SynchronizeObjects();
                    if (_isFirstLoad)
                    {
                        FirstLoad();
                        _isFirstLoad = false;
                    }
                }
                _doNotifyServer = true;
            }
            catch (Exception exc)
            {
                Log.Write(exc);
                throw;
            }
        }
        void DetectChange()
        {
            if (SocketClient != null)
                SocketClient.WaitForData(SocketClient.Socket);
        }
        public void InvokeStart(SocketClient sClient, Logs log)
        {
            Log = log;
            SocketClient = sClient;

            // Initialize
            if (SocketClient != null)
                SocketClient.DataReceived += new SocketPublisher.DataReceiveHandler(socketClient_DataReceived);

            this.components = new System.ComponentModel.Container();
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.timer1.Interval = 200;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);

            Thread readSocketThread = new Thread(this.DetectChange);
            readSocketThread.Start();

            timer1.Start();
            Start();
        }

        public void Merge<T>(byte[] byteData)
        {
            Model = ModelBase.Load<T>(byteData) as ModelBase;
            Model.ValueChanged += new Notification.ValueChangedEventHandler(Model_ValueChanged);
        }

        void Model_ValueChanged(object sender, Notification.ValueChangedEventArgs e)
        {
            if (_doNotifyServer)
                UpdateServer(new DataPacket<ValueChangedEventArgs>(e, new DataPacketHeader(Constants.ValueChanged)));
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            DoPeriodicTask();
        }
        protected void UpdateServer(DataPacket packet)
        {
            if (SocketClient != null)
                SocketClient.SendMessage(packet);
        }
        public abstract void Start();

        public abstract void DoPeriodicTask();

        protected override void Dispose(bool disposing)
        {
            Log.Dispose();
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        public abstract void SynchronizeObjects();

        public abstract void FirstLoad();
    }
}
